package com.net.xpay.common.util;


import com.google.common.base.Strings;
import com.net.common.exception.BusinessException;
import com.net.xpay.core.constant.PoseidonErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@Slf4j
public class OpenApiSignatureUtil {

    public static String rsaSign(String content, String privateKey, String charset, String signType) {
        if ("RSA".equals(signType)) {
            return rsa1Sign(content, privateKey, charset);
        } else if ("RSA2".equals(signType)) {
            return rsa256Sign(content, privateKey, charset);
        } else {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "Sign Type is Not Support : signType="  + signType);
        }
    }

    private static String rsa256Sign(String content, String privateKey, String charset) {
        try {
            PrivateKey priKey = getPrivateKeyFromPkcs8("RSA", new ByteArrayInputStream(privateKey.getBytes()));
            Signature signature = Signature.getInstance("SHA256WithRSA");
            signature.initSign(priKey);
            if (Strings.isNullOrEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }

            byte[] signed = signature.sign();
            return new String(Base64.encodeBase64(signed));
        } catch (Exception e) {
            log.info("rsa256Sign error|content={}", content,  e);
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "RSAContent = " + content + "; charset = " + charset);
        }
    }

    private static String rsa1Sign(String content, String privateKey, String charset) {
        try {
            PrivateKey priKey = getPrivateKeyFromPkcs8("RSA", new ByteArrayInputStream(privateKey.getBytes()));
            Signature signature = Signature.getInstance("SHA1WithRSA");
            signature.initSign(priKey);
            if (Strings.isNullOrEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }

            byte[] signed = signature.sign();
            return new String(Base64.encodeBase64(signed));
        } catch (InvalidKeySpecException e) {
            log.error("rsaSign error", e);
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "RSA私钥格式不正确，请检查是否正确配置了PKCS8格式的私钥");
        } catch (Exception e) {
            log.error("rsaSign error", e);
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "RSAContent = " + content + "; charset = " + charset);
        }
    }

    private static PrivateKey getPrivateKeyFromPkcs8(String algorithm, InputStream ins) throws Exception {
        if (ins != null && !Strings.isNullOrEmpty(algorithm)) {
            KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
            byte[] encodedKey = StreamUtils.readText(ins).getBytes();
            encodedKey = Base64.decodeBase64(encodedKey);
            return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
        } else {
            return null;
        }
    }


    public static String getSignCheckContent(Map<String, String> params) {
        if (params == null) {
            return null;
        } else {
            params.remove("sign");
            StringBuilder content = new StringBuilder();
            List<String> keys = new ArrayList<>(params.keySet());
            Collections.sort(keys);

            for(int i = 0; i < keys.size(); ++i) {
                String key = keys.get(i);
                String value = params.get(key);
                content.append(i == 0 ? "" : "&").append(key).append("=").append(value);
            }

            return content.toString();
        }
    }

    /**
     * rsa2方式验证签名
     */
    public static boolean rsaCheck(Map<String, String> params, String publicKey, String charset) {
        String sign = params.get("sign");
        String content = getSignCheckContent(params);
        return rsa256CheckContent(content, sign, publicKey, charset);
    }

    private static boolean rsa256CheckContent(String content, String sign, String publicKey, String charset) {
        try {
            PublicKey pubKey = getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes()));
            Signature signature = Signature.getInstance("SHA256WithRSA");
            signature.initVerify(pubKey);
            if (Strings.isNullOrEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }

            return signature.verify(Base64.decodeBase64(sign.getBytes()));
        } catch (Exception e) {
            log.error("rsa256CheckContent error|content={}|sign={}|publicKey={}|charset={}", content, sign, publicKey, charset, e);
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "签名校验异常");
        }
    }

    private static PublicKey getPublicKeyFromX509(String algorithm, InputStream ins) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        StringWriter writer = new StringWriter();
        StreamUtils.io(new InputStreamReader(ins), writer);
        byte[] encodedKey = writer.toString().getBytes();
        encodedKey = Base64.decodeBase64(encodedKey);
        return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
    }



}
